#include "SortSelBase.h"

#include "Classe.h"

#include "ATL.h"

#include <QtGui/QDialog>
#include <QtGui/QPushButton>
#include <QtGui/QVBoxLayout>
#include <QtGui/QHBoxLayout>
#include <QtGui/QSpinBox>
#include <QtGui/QLabel>
#include <QtGui/QGroupBox>
#include <QtGui/QButtonGroup>

#include <QtGui/QApplication>

#include <vector>

#include "Joueur.h"
#include "lib.h"
#include "Sort.h"
#include "ListeSorts.h"

#include "SelectionSort.h"

#include <boost/assign/list_of.hpp>

using namespace std;
using namespace boost::assign;

const vector< string > SortSelBase::Ecoles = list_of
  ("Abjuration")
  ("Altération")
  ("Conjuration - Convocation")
  ("Divination")
  ("Enchantement - Charme")
  ("Illusion - Fantasme")
  ("Invocation - Evocation")
  ("Nécromancie")
  ("Alchimie")
  ("Artifices")
  ("Dimension")
  ("Elémentaire Air")
  ("Elémentaire Eau")
  ("Elémentaire Feu")
  ("Elémentaire Terre")
  ("Eurythmique")
  ("Forces")
  ("Géométrie")
  ("Mentalisme")
  ("Ombres")
  ("Universelle");

/**
 * Constructeur par défault. Construit la liste des écoles, en regardant les
 * quelles sont disponibles d'après 1) la classe et 2) les compétences de
 * classe.
 * Les sorts déjà connus ne peuvent pas être sélectionnés à nouveau.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 *
 *
 * @param j joueur auquel on veut ajouter des sorts.
 */
SortSelBase::SortSelBase(Joueur *j)
  : BaseMultSel( Ecoles.size(), Ecoles, "Ecole de magie", "SortSelBase"),
    ecoleCourante( 0 ), classeCourante( 0 )
{
  /*Debug::*/empileNiveau( &interne );
  func() << "SortSelBase::SortSelBase -> début" << endmsg;
  joueur = j;
  
  unsigned niv( preparerListeEcoles() );

  while (! boutons.empty())
    {
      delete boutons.back();
      boutons.pop_back();
    }
  
  boutons.push_back( new QPushButton( trUtf8("&Quitter"), this ) );
  boutons.back() -> setToolTip( trUtf8("Abandonner le programme") );
  connect( boutons.back()  , SIGNAL( clicked() ), this, SLOT( annuler() ) );
  boutons.push_back( new QPushButton( trUtf8("&Terminer"), this ) );
  boutons.back() -> setToolTip( trUtf8("Quitter la sélection de sort") );
  connect( boutons.back()  , SIGNAL( clicked() ), this, SLOT( fermer() ) );
  boutons.push_back( new QPushButton( trUtf8("&OK"), this ) );
  boutons.back() -> setToolTip( trUtf8("Sélectionner les sorts de cette école") );
  connect( boutons.back(), SIGNAL( clicked() ), this, SLOT( selectionner() ) );

  choisirNiveau = new QSpinBox( this );
  choisirNiveau -> setMinimum( 1 );
  choisirNiveau -> setMaximum( niv );
  
  nivGroupe = new QGroupBox( trUtf8("Choix du niveau"), this );

  etiquette = new QLabel( trUtf8("Niveau"), this );

  QHBoxLayout *nivLayout = new QHBoxLayout( nivGroupe );

  nivLayout -> addWidget( etiquette );
  nivLayout -> addWidget( choisirNiveau );

  nivGroupe -> setLayout( nivLayout );

  fenetreLayout -> addWidget( nivGroupe );

  setFenetreLayout();


  func() << "SortSelBase::SortSelBase -> " << endmsg;
  /*Debug::*/depileNiveau();
}

/**
 * Constructeur de copie. Il faut récupérer le pointeur sur le Joueur
 * et ensuite on fait "normalement".
 *
 * @param sortSel instance de SortSelBase à dupliquer.
 */
SortSelBase::SortSelBase(const SortSelBase& sortSel)
 : BaseMultSel( sortSel ), ecoleCourante( 0 ), classeCourante( 0 )
{
 joueur = sortSel.joueur;

 unsigned niv( preparerListeEcoles() );
 /* 
  unsigned niv(0), classes(joueur -> nbrClasse()), i, k, nombre;

  string classe;

  accesEcoles.assign(21, false);

  vector< vector<unsigned> > tmp(9);

  sortsChoisis.assign(21, tmp);

  const vector<unsigned> *ptrVec;

  Classe *ptrClasse;

  // Ici on veut regarder quelles sont les écoles accesssibles...
  for (i = 0; i < classes; i++)
    {
      ptrClasse = joueur -> classePtr( classes - i - 1 );
      classe = ptrClasse -> classe();

      ptrVec = ptrClasse -> competences( nombre );

      if (classe == "Mage")
	{
	  niv = ptrClasse -> niveauMagie();
	  for (k = 0; k < nombre; k++)
	    {
	      if ( ptrVec -> at(k) < 8)
		{
		  linfo() << "A accès à : " << Ecoles[ptrVec -> at(k)]
			  << endmsg;
		  accesEcoles[ptrVec -> at(k)] = true;
		}
	    }
	  accesEcoles[20] = true;
	  classeCourante = classes - i - 1;
	  break;
	}
      else if (classe == "Spécialiste")
	{
	  niv = ptrClasse -> niveauMagie();
	  for (k = 0; k < nombre; k++)
	    {
	      if ( ptrVec -> at(k) < 20)
		{
		  linfo() << "A accès à : " << Ecoles[ptrVec -> at(k)]
			  << endmsg;
		  accesEcoles[ptrVec -> at(k)] = true;
		}
	    }
	  classeCourante = classes - i - 1;
	  break;
	}
      else if (classe == "Barde")
	{
	  niv = ptrClasse -> niveauMagie();
	  for (k = 0; k < nombre; k++)
	    {
	      if (ptrVec -> at(k) == 12)
		{
		  for (k = 0; k < 9; k++)
		    {
		      linfo() << "A accès à : " << Ecoles[ptrVec -> at(k)]
			      << endmsg;
		      accesEcoles[k] = true;
		    }
		  break;
		}
	    }
	  classeCourante = classes - i - 1;
	  break;
	}
    }

  sortsConnus = joueur -> sorts(nbrSortsConnus, classeCourante);

  for (i = 0; i < dimension; i++)
    groupe -> button(i) -> setEnabled( accesEcoles[i] );
  */

  while (! boutons.empty())
    {
      delete boutons.back();
      boutons.pop_back();
    }
  
  boutons.push_back( new QPushButton( trUtf8("&Quitter"), this ) );
  boutons.back() -> setToolTip( trUtf8("Abandonner le programme") );
  connect( boutons.back()  , SIGNAL( clicked() ), this, SLOT( annuler() ) );
  boutons.push_back( new QPushButton( trUtf8("&Terminer"), this ) );
  boutons.back() -> setToolTip( trUtf8("Quitter la sélection de sort") );
  connect( boutons.back()  , SIGNAL( clicked() ), this, SLOT( fermer() ) );
  boutons.push_back( new QPushButton( trUtf8("&OK"), this ) );
  boutons.back() -> setToolTip( trUtf8("Sélectionner les sorts de cette école") );
  connect( boutons.back(), SIGNAL( clicked() ), this, SLOT( selectionner() ) );

  choisirNiveau = new QSpinBox( this );
  choisirNiveau -> setMinimum( 1 );
  choisirNiveau -> setMaximum( niv );

  nivGroupe = new QGroupBox( trUtf8("Choix du niveau"), this );

  etiquette = new QLabel( trUtf8("Niveau"), this );

  QHBoxLayout *nivLayout = new QHBoxLayout( nivGroupe );

  nivLayout -> addWidget( etiquette );
  nivLayout -> addWidget( choisirNiveau );

  nivGroupe -> setLayout( nivLayout );

  fenetreLayout -> addWidget( nivGroupe );

  setFenetreLayout();
}

/**
 * Destructeur, ne fait rien de spécial.
 */
SortSelBase::~SortSelBase()
{}

unsigned SortSelBase::preparerListeEcoles()
{
  /*Debug::*/empileNiveau( &interne );
  func() << "SortSelBase::prerparerListeEcoles -> début" << endmsg;

  unsigned niv( 0 ), classes( joueur -> nbrClasses() ),
    i, k, nombre;

  string classe;

  accesEcoles.assign( 21, false );

  vector< vector<unsigned> > tmp(9);

  sortsChoisis.assign( 21, tmp );

  const vector<unsigned> *ptrVec;

  Classe *ptrClasse;

  // Ici on veut regarder quelles sont les écoles accesssibles...
  for ( i = 0; i < classes; i++ )
    {
      ptrClasse = joueur -> classePtr( classes - i - 1 );
      classe = ptrClasse -> classe();

      ptrVec = ptrClasse -> competences( nombre );

      if (classe == "Mage")
	{
	  niv = ptrClasse -> niveauMagie();
	  for (k = 0; k < nombre; k++)
	    {
	      if ( ptrVec -> at(k) < 8)
		{
		  linfo() << "A accès à : " << Ecoles[ptrVec -> at(k)]
			  << endmsg;
		  accesEcoles[ptrVec -> at(k)] = true;
		}
	    }
	  accesEcoles[20] = true;
	  classeCourante = classes - i - 1;
	  break;
	}
      else if (classe == "Spécialiste")
	{
	  niv = ptrClasse -> niveauMagie();
	  for (k = 0; k < nombre; k++)
	    {
	      if ( ptrVec -> at(k) < 20)
		{
		  linfo() << "A accès à : " << Ecoles[ptrVec -> at(k)]
			  << endmsg;
		  accesEcoles[ptrVec -> at(k)] = true;
		}
	    }
	  classeCourante = classes - i - 1;
	  break;
	}
      else if (classe == "Barde")
	{
	  niv = ptrClasse -> niveauMagie();
	  for (k = 0; k < nombre; k++)
	    {
	      if (ptrVec -> at(k) == 12)
		{
		  for (k = 0; k < 9; k++)
		    {
		      linfo() << "A accès à : " << Ecoles[ptrVec -> at(k)]
			      << endmsg;
		      accesEcoles[k] = true;
		    }
		  break;
		}
	    }
	  classeCourante = classes - i - 1;
	  break;
	}
    }

  sortsConnus = joueur -> sorts( nbrSortsConnus, classeCourante );

  for (i = 0; i < dimension; i++)
    groupe -> button(i) -> setEnabled( accesEcoles[i] );

  func() << "SortSelBase::prerparerListeEcoles -> fin" << endmsg;
  /*Debug::*/depileNiveau();

  return niv;
}

/** Récupère la liste des sorts de l'école désirée.
 * L'école est donnée par le numéro du checkbox qui est sélectionné,
 * on lit alors le nombre de sorts depuis le conteneur approprié,
 * définit dans ListeSorts.h
 *
 * @param nbr est le nombre de sorts dans l'école, écrit dans la
 * méthode.
 * @return un pointeur sur le premier sort de la liste.
 */
const SortsEcoles* SortSelBase::donnerListeSort(unsigned& nbr)
{
  // Trouver la catégorie sélectionnée
  unsigned i;
  
  for (i = 0; i < dimension; i++)
    {
      if (groupe -> button(i) -> isChecked())
	{
	  ecoleCourante = i;
	  break;
	}
    }


  switch (ecoleCourante)
    {
    case 0:
      nbr = Abjuration . size();
      return &Abjuration[0];
    case 1:
      nbr = Alteration . size();
      return &Alteration[0];
    case 2:
      nbr = Conjuration . size();
      return &Conjuration[0];
    case 3:
      nbr = Divination . size();
      return &Divination[0];
    case 4:
      nbr = Enchantement . size();
      return &Enchantement[0];
    case 5:
      nbr = Illusion . size();
      return &Illusion[0];
    case 6:
      nbr = Invocation . size();
      return &Invocation[0];
    case 7:
      nbr = Necromancie . size();
      return &Necromancie[0];
    case 20:
      nbr = Universelle . size();
      return &Universelle[0];
    case 8:
      nbr = Alchimie . size();
      return &Alchimie[0];
    case 9:
      nbr = Artifices . size();
      return &Artifices[0];
    case 10:
      nbr = Dimension . size();
      return &Dimension[0];
    case 11:
      nbr = ElementAir . size();
      return &ElementAir[0];
    case 12:
      nbr = ElementEau . size();
      return &ElementEau[0];
    case 13:
      nbr = ElementFeu . size();
      return &ElementFeu[0];
    case 14:
      nbr = ElementTerre . size();
      return &ElementTerre[0];
    case 15:
      nbr = Eurythmique . size();
      return &Eurythmique[0];
    case 16:
      nbr = Forces . size();
      return &Forces[0];
    case 17:
      nbr = Geometrie . size();
      return &Geometrie[0];
    case 18:
      nbr = Mentalisme . size();
      return &Mentalisme[0];
    case 19:
      nbr = Ombres . size();
      return &Ombres[0];
    default:
      nbr = 0;
      return 0;
    }
}

/** Permet de sélectionner une école de magie.
 * Est appelé lorsque le bouton "OK" est pressé. On crée la liste des
 * sorts correspondants à l'école choisie et du niveau voulu. Les
 * sorts déjà appris sont repérés afin d'éviter de les sélectionner
 * deux fois.
 *
 * Ensuite, on crée un objet de type SelectionSort qui permet de
 * sélectionner les sorts parmi la liste construite.
 *
 * On stocke le choix validé dans le tenseur sortsChoisis.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 * - \e DEBUG affiche le contenu du message renvoyé par
 * SelectionSort::selectionFaite.
 */
void SortSelBase::selectionner()
{
  /*Debug::*/empileNiveau( &interne );
  func() << "SortSelBase::selectionner -> début" << endmsg;

  unsigned nombre, i, niveau(choisirNiveau -> value());

  const SortsEcoles *sorts = donnerListeSort(nombre);

  vector<string> nomSorts;

  for (i = 0; i < nombre; i++)
    {
      if ( (*(sorts + i)).niveau() == niveau )
	{
	  nomSorts.push_back( (*(sorts + i)).nom() );
	}
    }

  string nomGroupe( Ecoles[ecoleCourante] );
  nomGroupe += " - niveau " + ecrireString(niveau);

  vector<bool> deja(nomSorts.size(), false);

  Sort tmp;

  for (i = 0; i < nbrSortsConnus; i++)
    {
      tmp = sortsConnus -> at(i);
      if (tmp.ecole() != ecoleCourante) continue;
      if (tmp.niveau() != niveau) continue;
      deja[ tmp.numero() ] = true;
    }

  SelectionSort nouveauxSorts( nomSorts.size(), &nomSorts[0], deja,
			       nomGroupe, this );

  vector<unsigned> choix;

  StatusCode sc( nouveauxSorts . selectionFaite( choix ) );

  debug() << sc.message() << endmsg;

  if (sc)
    {
      sortsChoisis[ecoleCourante][niveau].assign(choix.begin(), choix.end());
    }

  func() << "SortSelBase::selectionner -> fin" << endmsg;
  /*Debug::*/depileNiveau();
}

/** Méthode permettant de récupérer les sorts que l'on a ajouté au
 * personnage.
 * On affiche la fenêtre, et ensuite on lit le tenseur sortChoisis
 * pour créer un conteneur compatible avec le conteneur des sorts. On
 * ajoute alors ce conteneur aux sorts déjà connus par la classe.
 *
 * Le niveau de sortie est paramétré par BaseUI::interne :\n
 * - \e INFO affiche le nombre de sorts ajouté.\n
 * - \e FUNC affiche les messages de début et de fin de méthode.\n
 *
 * @param vec conteneur des sorts ajoutés (à quoi sert-il?).
 * @return StatusCode \e true si la sélection est faite, \e false si
 * annulée, le nombre de sorts ajoutés est donné par valeur().
 */
StatusCode SortSelBase::sortsSelectionnes(vector<unsigned>& vec)
{
  /*Debug::*/empileNiveau( &interne );
  func() << "SortSelBase::sortsSelectionnes -> " << endmsg;
  this -> show();

  qApp -> exec();

  unsigned niveaux, taille, j, k;
  
  vector<Sort> nouveauxSorts;

  for ( unsigned i(0); i < dimension; i++ )
    {
      niveaux = sortsChoisis[i].size();
      for (j = 0; j < niveaux; j++)
	{
	  taille = sortsChoisis[i][j].size();
	  for (k = 0; k < taille; k++)
	    {
	      vec.push_back(i); // école
	      vec.push_back(j); // niveau
	      vec.push_back(sortsChoisis[i][j][k]);
	      nouveauxSorts.push_back(Sort(i, j, sortsChoisis[i][j][k]));
	    }
	}
    }

  info() << nouveauxSorts.size() << " ajoutés à la classe " << classeCourante
	 << endmsg;

  joueur -> classePtr( classeCourante ) -> ajoutSorts( nouveauxSorts );

  func() << "SortSelBase::sortsSelectionnes -> " << endmsg;
  /*Debug::*/depileNiveau();

  return StatusCode(OK, vec.size());
}
